using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;

namespace HyxFrame.Core
{

    /// <summary>
    /// 对象池管理器，用于管理和复用游戏对象，减少实例化和销毁操作，提高性能
    /// </summary>
    public class ObjectPoolManager : MonoSingleton<ObjectPoolManager>
    {
        #region Fields
        /// <summary>
        /// 对象池字典，存储不同预制体的对象队列
        /// Key: 预制体名称，Value: 对象队列
        /// </summary>
        private readonly Dictionary<string, Queue<GameObject>> poolDictionary = new Dictionary<string, Queue<GameObject>>();

        /// <summary>
        /// 资源引用字典，存储Addressables加载的预制体句柄
        /// Key: 预制体名称，Value: 异步操作句柄
        /// </summary>
        private readonly Dictionary<string, AsyncOperationHandle<GameObject>> res_PrefabDic = new Dictionary<string, AsyncOperationHandle<GameObject>>();

        /// <summary>
        /// 预制体缓存字典，存储已加载的预制体
        /// Key: 预制体名称，Value: 预制体对象
        /// </summary>
        private readonly Dictionary<string, GameObject> dic_Prefabs = new Dictionary<string, GameObject>();

        /// <summary>
        /// 池外对象列表，记录当前正在使用的对象
        /// </summary>
        private readonly List<GameObject> outsidePool = new List<GameObject>();

        /// <summary>
        /// 对象池最大容量
        /// </summary>
        private const int MAX_POOL_SIZE = 500;
        #endregion

        #region Unity Lifecycle
        /// <summary>
        /// 初始化管理器
        /// </summary>
        protected override void InitManager()
        {
            // 字典已在声明时初始化
        }

        /// <summary>
        /// 启用时注册场景加载事件
        /// </summary>
        private void OnEnable()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }

        /// <summary>
        /// 禁用时取消注册场景加载事件
        /// </summary>
        private void OnDisable()
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
        }
        #endregion

        #region Scene Management
        /// <summary>
        /// 场景加载完成时的回调
        /// </summary>
        /// <param name="scene">加载的场景</param>
        /// <param name="mode">场景加载模式</param>
        private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            // 清空对象池
            ClearPool();
            // 卸载未使用的资源
            UnloadUnusedAssets();
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// 实例化预制体（使用GameObject参数）
        /// </summary>
        /// <param name="prefab">要实例化的预制体</param>
        /// <param name="callback">实例化完成后的回调</param>
        public void InstantiatePrefab(GameObject prefab, System.Action<GameObject> callback)
        {
            // 参数验证
            if (prefab == null)
            {
                Debug.LogError("Prefab is null");
                callback?.Invoke(null);
                return;
            }

            string prefabName = prefab.name;
            // 确保对象池存在
            InitializePoolIfNotExists(prefabName);

            // 尝试从对象池获取对象
            if (TryGetObjectFromPool(prefabName, out GameObject pooledObject))
            {
                callback?.Invoke(pooledObject);
            }
            else
            {
                // 对象池为空，创建新对象
                CreateNewObject(prefab, callback);
            }

            // 维护对象池大小
            MaintainPoolSize(poolDictionary[prefabName]);
        }

        /// <summary>
        /// 实例化预制体（使用字符串key）
        /// </summary>
        /// <param name="prefabKey">预制体的资源key</param>
        /// <param name="callback">实例化完成后的回调</param>
        public void InstantiatePrefab(string prefabKey, System.Action<GameObject> callback)
        {
            // 参数验证
            if (string.IsNullOrEmpty(prefabKey))
            {
                Debug.LogError("Prefab key is null or empty");
                callback?.Invoke(null);
                return;
            }

            // 确保对象池存在
            InitializePoolIfNotExists(prefabKey);

            // 尝试从对象池获取对象
            if (TryGetObjectFromPool(prefabKey, out GameObject pooledObject))
            {
                callback?.Invoke(pooledObject);
            }
            // 检查预制体是否已加载
            else if (dic_Prefabs.TryGetValue(prefabKey, out GameObject prefab))
            {
                CreateNewObject(prefab, callback);
            }
            else
            {
                // 加载预制体
                LoadPrefab(prefabKey, callback);
            }

            // 维护对象池大小
            MaintainPoolSize(poolDictionary[prefabKey]);
        }

        /// <summary>
        /// 将对象返回到对象池
        /// </summary>
        /// <param name="pooledObject">要返回的对象</param>
        public void ReturnToPool(GameObject pooledObject)
        {
            if (pooledObject == null) return;

            // 检查对象是否属于当前对象池
            if (pooledObject.TryGetComponent<ObjectPoolItem>(out var poolItem) && poolItem.PoolManager == this)
            {
                pooledObject.SetActive(false);
                outsidePool.Remove(pooledObject);

                // 将对象返回到对应的对象池
                if (poolDictionary.TryGetValue(pooledObject.name, out var queue))
                {
                    queue.Enqueue(pooledObject);
                }
                else
                {
                    // 如果找不到对应的对象池，销毁对象
                    Destroy(pooledObject);
                }
            }
            else
            {
                // 对象不属于当前对象池，直接销毁
                Destroy(pooledObject);
            }
        }

        /// <summary>
        /// 将所有池外对象返回到对象池
        /// </summary>
        public void AllReturnPool()
        {
            // 反向遍历，避免集合修改问题
            for (int i = outsidePool.Count - 1; i >= 0; i--)
            {
                if (outsidePool[i] != null)
                {
                    outsidePool[i].SetActive(false);
                    ReturnToPool(outsidePool[i]);
                }
            }
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// 如果对象池不存在则初始化
        /// </summary>
        /// <param name="key">对象池的key</param>
        private void InitializePoolIfNotExists(string key)
        {
            if (!poolDictionary.ContainsKey(key))
            {
                poolDictionary[key] = new Queue<GameObject>();
            }
        }

        /// <summary>
        /// 尝试从对象池获取对象
        /// </summary>
        /// <param name="key">对象池的key</param>
        /// <param name="pooledObject">获取到的对象</param>
        /// <returns>是否成功获取</returns>
        private bool TryGetObjectFromPool(string key, out GameObject pooledObject)
        {
            pooledObject = null;
            if (poolDictionary.TryGetValue(key, out var queue) && queue.Count > 0)
            {
                pooledObject = queue.Dequeue();
                pooledObject.SetActive(true);
                outsidePool.Add(pooledObject);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 创建新的对象实例
        /// </summary>
        /// <param name="prefab">预制体</param>
        /// <param name="callback">创建完成后的回调</param>
        private void CreateNewObject(GameObject prefab, System.Action<GameObject> callback)
        {
            GameObject newObject = Instantiate(prefab);
            newObject.name = prefab.name;

            // 确保对象有ObjectPoolItem组件
            if (!newObject.TryGetComponent<ObjectPoolItem>(out _))
            {
                newObject.AddComponent<ObjectPoolItem>().PoolManager = this;
            }

            outsidePool.Add(newObject);
            callback?.Invoke(newObject);
        }

        /// <summary>
        /// 加载预制体资源
        /// </summary>
        /// <param name="prefabKey">预制体的资源key</param>
        /// <param name="callback">加载完成后的回调</param>
        private void LoadPrefab(string prefabKey, System.Action<GameObject> callback)
        {
            // 检查是否已经在加载中
            if (res_PrefabDic.ContainsKey(prefabKey))
            {
                Debug.LogError($"Prefab with key {prefabKey} is already being loaded");
                callback?.Invoke(null);
                return;
            }

            // 开始异步加载
            res_PrefabDic[prefabKey] = Addressables.LoadAssetAsync<GameObject>(prefabKey);
            dic_Prefabs[prefabKey] = null;

            // 设置加载完成回调
            res_PrefabDic[prefabKey].Completed += op =>
            {
                if (op.Status == AsyncOperationStatus.Succeeded)
                {
                    // 加载成功，缓存预制体
                    dic_Prefabs[prefabKey] = op.Result;
                    CreateNewObject(op.Result, callback);
                }
                else
                {
                    // 加载失败
                    Debug.LogError($"Failed to load prefab with key {prefabKey}");
                    callback?.Invoke(null);
                }
            };
        }

        /// <summary>
        /// 维护对象池大小，确保不超过最大容量
        /// </summary>
        /// <param name="objectQueue">要维护的对象队列</param>
        private void MaintainPoolSize(Queue<GameObject> objectQueue)
        {
            while (objectQueue.Count > MAX_POOL_SIZE)
            {
                Destroy(objectQueue.Dequeue());
            }
        }

        /// <summary>
        /// 清空所有对象池
        /// </summary>
        private void ClearPool()
        {
            foreach (var pool in poolDictionary.Values)
            {
                while (pool.Count > 0)
                {
                    Destroy(pool.Dequeue());
                }
            }
            poolDictionary.Clear();
        }

        /// <summary>
        /// 卸载未使用的资源
        /// </summary>
        private void UnloadUnusedAssets()
        {
            foreach (var kvp in res_PrefabDic)
            {
                if (kvp.Value.IsValid())
                {
                    Addressables.Release(kvp.Value);
                }
            }
            res_PrefabDic.Clear();
            dic_Prefabs.Clear();
            outsidePool.Clear();
        }
        #endregion
    }
}